/* Copyright (c) 2005-2006 Russ Cox, MIT; see COPYRIGHT */

#include <stdio.h>
#include "taskimpl.h"

#if defined(__APPLE__)
#if defined(__i386__)
#define NEEDX86MAKECONTEXT
#define NEEDSWAPCONTEXT
#elif defined(__x86_64__)
#define NEEDAMD64MAKECONTEXT
#define NEEDSWAPCONTEXT
#elif defined(__arm64__) || defined(__aarch64__)
#define NEEDARM64MAKECONTEXT
#define NEEDSWAPCONTEXT
#else
#define NEEDPOWERMAKECONTEXT
#define NEEDSWAPCONTEXT
#endif
#endif

#if defined(__FreeBSD__) && defined(__i386__) && __FreeBSD__ < 5
#define NEEDX86MAKECONTEXT
#define NEEDSWAPCONTEXT
#endif

#if defined(__OpenBSD__) && defined(__i386__)
#define NEEDX86MAKECONTEXT
#define NEEDSWAPCONTEXT
#endif

#if defined(__linux__) && defined(__arm__)
#define NEEDSWAPCONTEXT
#define NEEDARMMAKECONTEXT
#endif

#ifdef NEEDPOWERMAKECONTEXT
void makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...) {
    ulong *sp, *tos;
    va_list arg;

    tos = (ulong *)ucp->uc_stack.ss_sp + ucp->uc_stack.ss_size / sizeof(ulong);
    sp = tos - 16;
    ucp->mc.pc = (long)func;
    ucp->mc.sp = (long)sp;
    va_start(arg, argc);
    ucp->mc.r3 = va_arg(arg, long);
    va_end(arg);
}
#endif

#ifdef NEEDX86MAKECONTEXT
void makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...) {
    int *sp;
    int i;
    va_list arg;

    /* Set sp to end of allocated stack area */
    sp = (int *)ucp->uc_stack.ss_sp + ucp->uc_stack.ss_size / 4;
    /* reserve space for argc ints */
    sp -= argc;
    sp = (void *)((uintptr_t)sp - (uintptr_t)sp % 16); /* 16-align for OS X */

    /* Copy arguments into reserved space on stack */
    va_start(arg, argc);
    for (i = 0; i < argc; i++)
        sp[i] = va_arg(arg, int);
    va_end(arg);

    *--sp = 0; /* return address */
    ucp->uc_mcontext.mc_eip = (long)func;
    ucp->uc_mcontext.mc_esp = (int)sp;
}
#endif

#ifdef NEEDAMD64MAKECONTEXT
void makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...) {
    long *sp;
    va_list va;

    memset(&ucp->uc_mcontext, 0, sizeof ucp->uc_mcontext);
    if (argc != 2) {
        // printf("%s", "makecontext missing argument\n");
        //*(int*)0 = 0;
        exit(-1);
    }
    va_start(va, argc);
    ucp->uc_mcontext.mc_rdi = va_arg(va, int);
    ucp->uc_mcontext.mc_rsi = va_arg(va, int);
    va_end(va);
    sp = (long *)ucp->uc_stack.ss_sp + ucp->uc_stack.ss_size / sizeof(long);
    sp -= argc;
    sp = (void *)((uintptr_t)sp - (uintptr_t)sp % 16); /* 16-align for OS X */
    *--sp = 0;                                         /* return address */
    ucp->uc_mcontext.mc_rip = (long)func;
    ucp->uc_mcontext.mc_rsp = (long)sp;
}
#endif

#ifdef NEEDARMMAKECONTEXT
void makecontext(ucontext_t *uc, void (*fn)(void), int argc, ...) {
    int i, *sp;
    va_list arg;

    sp = (int *)uc->uc_stack.ss_sp + uc->uc_stack.ss_size / 4;
    va_start(arg, argc);

    if (argc-- > 0)
        uc->uc_mcontext.arm_r0 = va_arg(arg, uint);
    if (argc-- > 0)
        uc->uc_mcontext.arm_r1 = va_arg(arg, uint);
    if (argc-- > 0)
        uc->uc_mcontext.arm_r2 = va_arg(arg, uint);
    if (argc-- > 0)
        uc->uc_mcontext.arm_r3 = va_arg(arg, uint);

    va_end(arg);
    uc->uc_mcontext.arm_sp = (uint)sp;
    uc->uc_mcontext.arm_lr = (uint)fn;
}
#endif


#ifdef NEEDSWAPCONTEXT
int swapcontext(ucontext_t *oucp, const ucontext_t *ucp) {
    if (getcontext(oucp) == 0)
        setcontext(ucp);
    return 0;
}
#endif
